/*
 * Copyright (C) 2012 Richard Schilling
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package dalvik.vm.analysis;

import static dalvik.vm.analysis.DisplayFlag.DRT_SHOW_LIVENESS;
import static dalvik.vm.analysis.RegisterType.kRegTypeBoolean;
import static dalvik.vm.analysis.RegisterType.kRegTypeByte;
import static dalvik.vm.analysis.RegisterType.kRegTypeChar;
import static dalvik.vm.analysis.RegisterType.kRegTypeConflict;
import static dalvik.vm.analysis.RegisterType.kRegTypeConstByte;
import static dalvik.vm.analysis.RegisterType.kRegTypeConstChar;
import static dalvik.vm.analysis.RegisterType.kRegTypeConstHi;
import static dalvik.vm.analysis.RegisterType.kRegTypeConstInteger;
import static dalvik.vm.analysis.RegisterType.kRegTypeConstLo;
import static dalvik.vm.analysis.RegisterType.kRegTypeConstPosByte;
import static dalvik.vm.analysis.RegisterType.kRegTypeConstPosShort;
import static dalvik.vm.analysis.RegisterType.kRegTypeConstShort;
import static dalvik.vm.analysis.RegisterType.kRegTypeDoubleHi;
import static dalvik.vm.analysis.RegisterType.kRegTypeDoubleLo;
import static dalvik.vm.analysis.RegisterType.kRegTypeFloat;
import static dalvik.vm.analysis.RegisterType.kRegTypeInteger;
import static dalvik.vm.analysis.RegisterType.kRegTypeLongHi;
import static dalvik.vm.analysis.RegisterType.kRegTypeLongLo;
import static dalvik.vm.analysis.RegisterType.kRegTypeOne;
import static dalvik.vm.analysis.RegisterType.kRegTypePosByte;
import static dalvik.vm.analysis.RegisterType.kRegTypePosShort;
import static dalvik.vm.analysis.RegisterType.kRegTypeShort;
import static dalvik.vm.analysis.RegisterType.kRegTypeUninit;
import static dalvik.vm.analysis.RegisterType.kRegTypeUnknown;
import static dalvik.vm.analysis.RegisterType.kRegTypeZero;
import dalvik.vm.analysis.CodeVerify.RegType;

// TODO if you format this file with your editor make sure the pretty looking tables aren't mangled.

/**
 * Constants used by the CodeVerify class.
 * 
 * @author rschilling
 */
public class CodeVerifyConstants {

    {
        checkMergeTab();
    }

    /**
     * Set this to enable dead code scanning. This is not required, but it's
     * very useful when testing changes to the verifier (to make sure we're not
     * skipping over stuff) and for checking the optimized output from "dx". The
     * only reason not to do it is that it slightly increases the time required
     * to perform verification.
     */
    public static final boolean DEAD_CODE_SCAN = true;
    public static final boolean gDebugVerbose = false;

    public static final int SHOW_REG_DETAILS = 0 | DRT_SHOW_LIVENESS.intValue();
    // | DRT_SHOW_REF_TYPES.intValue() | DRT_SHOW_LOCALS.intValue()

    /**
     * We need an extra "pseudo register" to hold the return type briefly. It
     * can be category 1 or 2, so we need two slots.
     */
    public static final int kExtraRegs = 2;
    // #define RESULT_REGISTER(_insnRegCount) (_insnRegCount)
    // TODO how is RESULT_REGISTER used?

    public static final RegisterType kRegType1nrSTART = RegisterType.kRegTypeZero;
    public static final RegisterType kRegType1nrEND = RegisterType.kRegTypeFloat;

    public static final int kRegTypeUninitMask = 0xff;
    public static final int kRegTypeUninitShift = 8;
    public static final int kMaxMonitorStackDepth = 8; // monitor entries

    public static final int kUninitThisArgAddr = -1;
    public static final int kUninitThisArgSlot = 0;
    public static final int kInsnFlagWidthMask = 0x0000ffff;
    public static final int kInsnFlagInTry = (1 << 16);
    public static final int kInsnFlagBranchTarget = (1 << 17);
    public static final int kInsnFlagGcPoint = (1 << 18);
    public static final int kInsnFlagVisited = (1 << 30);
    public static final int kInsnFlagChanged = (1 << 31);

    /* constants used in the table below for easy import. */

    public static final RegisterType __ = kRegTypeUnknown;
    public static final RegisterType _U = kRegTypeUninit;
    public static final RegisterType _X = kRegTypeConflict;
    public static final RegisterType _0 = kRegTypeZero;
    public static final RegisterType _1 = kRegTypeOne;
    public static final RegisterType _Z = kRegTypeBoolean;
    public static final RegisterType _y = kRegTypeConstPosByte;
    public static final RegisterType _Y = kRegTypeConstByte;
    public static final RegisterType _h = kRegTypeConstPosShort;
    public static final RegisterType _H = kRegTypeConstShort;
    public static final RegisterType _c = kRegTypeConstChar;
    public static final RegisterType _i = kRegTypeConstInteger;
    public static final RegisterType _b = kRegTypePosByte;
    public static final RegisterType _B = kRegTypeByte;
    public static final RegisterType _s = kRegTypePosShort;
    public static final RegisterType _S = kRegTypeShort;
    public static final RegisterType _C = kRegTypeChar;
    public static final RegisterType _I = kRegTypeInteger;
    public static final RegisterType _F = kRegTypeFloat;
    public static final RegisterType _N = kRegTypeConstLo;
    public static final RegisterType _n = kRegTypeConstHi;
    public static final RegisterType _J = kRegTypeLongLo;
    public static final RegisterType _j = kRegTypeLongHi;
    public static final RegisterType _D = kRegTypeDoubleLo;
    public static final RegisterType _d = kRegTypeDoubleHi;

    /*
     * Merge result table for primitive values. The table is symmetric along
     * the diagonal.
     * 
     * <p>Note that 32-bit int/float do not merge into 64-bit long/double. This
     * is a register merge, not a widening conversion. Only the "implicit"
     * widening within a category, e.g. byte to short, is allowed.
     * 
     * <p>Dalvik does not draw a distinction between int and float, but we
     * enforce
     * that once a value is used as int, it can't be used as float, and vice
     * versa. We do not allow free exchange between 32-bit int/float and 64-bit
     * long/double.
     * 
     * <p>Note that Uninit+Uninit=Uninit. This holds true because we only
     * use this when the RegType value is exactly equal to kRegTypeUninit, which
     * can only happen for the zeroeth entry in the table.
     * 
     * <p>"Unknown" never merges with anything known. The only time a register
     * transitions from "unknown" to "known" is when we're executing code
     * for the first time, and we handle that with a simple copy.
     */
// @formatter:off    
    public static final RegisterType gDvmMergeTab[][] = 
    {
        /* chk:  _  U  X  0  1  Z  y  Y  h  H  c  i  b  B  s  S  C  I  F  N  n  J  j  D  d */
        { /*_*/ __,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
        { /*U*/ _X,_U,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
        { /*X*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X },
        { /*0*/ _X,_X,_X,_0,_Z,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*1*/ _X,_X,_X,_Z,_1,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*Z*/ _X,_X,_X,_Z,_Z,_Z,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*y*/ _X,_X,_X,_y,_y,_y,_y,_Y,_h,_H,_c,_i,_b,_B,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*Y*/ _X,_X,_X,_Y,_Y,_Y,_Y,_Y,_h,_H,_c,_i,_B,_B,_S,_S,_I,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*h*/ _X,_X,_X,_h,_h,_h,_h,_h,_h,_H,_c,_i,_s,_S,_s,_S,_C,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*H*/ _X,_X,_X,_H,_H,_H,_H,_H,_H,_H,_c,_i,_S,_S,_S,_S,_I,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*c*/ _X,_X,_X,_c,_c,_c,_c,_c,_c,_c,_c,_i,_C,_I,_C,_I,_C,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*i*/ _X,_X,_X,_i,_i,_i,_i,_i,_i,_i,_i,_i,_I,_I,_I,_I,_I,_I,_F,_X,_X,_X,_X,_X,_X },
        { /*b*/ _X,_X,_X,_b,_b,_b,_b,_B,_s,_S,_C,_I,_b,_B,_s,_S,_C,_I,_X,_X,_X,_X,_X,_X,_X },
        { /*B*/ _X,_X,_X,_B,_B,_B,_B,_B,_S,_S,_I,_I,_B,_B,_S,_S,_I,_I,_X,_X,_X,_X,_X,_X,_X },
        { /*s*/ _X,_X,_X,_s,_s,_s,_s,_S,_s,_S,_C,_I,_s,_S,_s,_S,_C,_I,_X,_X,_X,_X,_X,_X,_X },
        { /*S*/ _X,_X,_X,_S,_S,_S,_S,_S,_S,_S,_I,_I,_S,_S,_S,_S,_I,_I,_X,_X,_X,_X,_X,_X,_X },
        { /*C*/ _X,_X,_X,_C,_C,_C,_C,_I,_C,_I,_C,_I,_C,_I,_C,_I,_C,_I,_X,_X,_X,_X,_X,_X,_X },
        { /*I*/ _X,_X,_X,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_I,_X,_X,_X,_X,_X,_X,_X },
        { /*F*/ _X,_X,_X,_F,_F,_F,_F,_F,_F,_F,_F,_F,_X,_X,_X,_X,_X,_X,_F,_X,_X,_X,_X,_X,_X },
        { /*N*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_N,_X,_J,_X,_D,_X },
        { /*n*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_n,_X,_j,_X,_d },
        { /*J*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_J,_X,_J,_X,_X,_X },
        { /*j*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_j,_X,_j,_X,_X },
        { /*D*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_D,_X,_X,_X,_D,_X },
        { /*d*/ _X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_X,_d,_X,_X,_X,_d },
    };
    
    public static final int[][] convTab  =
        {
            /* chk: 0  1  Z  y  Y  h  H  c  i  b  B  s  S  C  I  F */
            { /*0*/ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
            { /*1*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
            { /*Z*/ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
            { /*y*/ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
            { /*Y*/ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 },
            { /*h*/ 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1 },
            { /*H*/ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1 },
            { /*c*/ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1 },
            { /*i*/ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1 },
            { /*b*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 },
            { /*B*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0 },
            { /*s*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 },
            { /*S*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 },
            { /*C*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
            { /*I*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
            { /*F*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
        };
// @formatter:on 

    /**
     * Determine whether we can convert "srcType" to "checkType", where
     * "checkType" is one of the category-1 non-reference types.
     * <p>
     * Constant derived types may become floats, but other values may not.
     */
    public boolean canConvertTo1nr(RegType srcType, RegType checkType) {

        if (checkType.register.asInt() < kRegType1nrSTART.asInt()
                || checkType.register.asInt() > kRegType1nrEND.asInt())
            throw new IllegalArgumentException(
                    "checkType must be between kRegType1nrSTART and kRegType1nrEND");

        if (srcType.register.asInt() >= kRegType1nrSTART.asInt()
                && srcType.register.asInt() <= kRegType1nrEND.asInt()) {

            // use x and y for readibilty.
            int x = srcType.register.asInt() - kRegType1nrSTART.asInt();
            int y = checkType.register.asInt() - kRegType1nrSTART.asInt();
            return convTab[x][y] == 1 ? true : false;
        }

        return false;
    }

    /**
     * Determine whether the category-2 types are compatible.
     */
    static boolean canConvertTo2(RegType srcType, RegType checkType) {
        return ((srcType.register.asInt() == kRegTypeConstLo.asInt() || srcType.register.asInt() == checkType.register
                .asInt()) && (checkType.register.asInt() == kRegTypeLongLo.asInt() || checkType.register
                .asInt() == kRegTypeDoubleLo.asInt()));
    }

    /**
     * Verify symmetry in the conversion table.
     */
    public static void checkMergeTab() {
        int i, j;

        for (i = 0; i < gDvmMergeTab.length; i++) {
            for (j = i; j < gDvmMergeTab[i].length; j++) {
                if (gDvmMergeTab[i][j] != gDvmMergeTab[j][i]) {
                    throw new IllegalStateException(String.format(
                            "Symmetry violation: %d,%d vs %d,%d", i, j, j, i));
                }
            }
        }
    }

    /**
     * Determine whether or not "instrType" and "targetType" are compatible,
     * for purposes of getting or setting a value in a field or array. The
     * idea is that an instruction with a category 1nr type (say, aget-short
     * or iput-boolean) is accessing a static field, instance field, or array
     * entry, and we want to make sure sure that the operation is legal.
     * At a minimum, source and destination must have the same width. We
     * further refine this to assert that "short" and "char" are not
     * compatible, because the sign-extension is different on the "get"
     * operations.
     * We're not considering the actual contents of the register, so we'll
     * never get "pseudo-types" like kRegTypeZero or kRegTypePosShort. We
     * could get kRegTypeUnknown in "targetType" if a field or array class
     * lookup failed. Category 2 types and references are checked elsewhere.
     */
    static boolean checkFieldArrayStore1nr(RegType instrType, RegType targetType) {
        return (instrType == targetType);
    }

    /**
     * Convert a VM PrimitiveType enum value to the equivalent RegType value.
     */
    static RegisterType primitiveTypeToRegType(PrimitiveType primType) {
        switch (primType) {
            case PRIM_BOOLEAN:
                return kRegTypeBoolean;
            case PRIM_BYTE:
                return kRegTypeByte;
            case PRIM_SHORT:
                return kRegTypeShort;
            case PRIM_CHAR:
                return kRegTypeChar;
            case PRIM_INT:
                return kRegTypeInteger;
            case PRIM_LONG:
                return kRegTypeLongLo;
            case PRIM_FLOAT:
                return kRegTypeFloat;
            case PRIM_DOUBLE:
                return kRegTypeDoubleLo;
            case PRIM_VOID:
            default: {
                assert (false);
                return kRegTypeUnknown;
            }
        }
    }
}
